﻿MAX_TARGET_DEBUFFS = 16;
MAX_TARGET_BUFFS = 32;

local AURA_START_X = 5;
local AURA_START_Y = 32;
local AURA_OFFSET_Y = 3;
local LARGE_AURA_SIZE = 28;	    --放大buff,默认21
local SMALL_AURA_SIZE = 17;
local AURA_ROW_WIDTH = 122;
local TOT_AURA_ROW_WIDTH = 101;
local NUM_TOT_AURA_ROWS = 2;

local largeBuffList = {};
local largeDebuffList = {};

hooksecurefunc("TargetFrame_OnUpdate", function(self)
	TargetFrame_UpdateAuras (self)
end)

function TargetFrame_UpdateAuras (self)
	local button, buttonName;
	local buttonIcon, buttonCount, buttonCooldown, buttonStealable, buttonBorder;

	local name, rank, icon, count, debuffType, duration, expirationTime, isMine, isStealable;
	local playerIsTarget = UnitIsUnit("player", "target");

	local numBuffs = 0;
	for i=1, MAX_TARGET_BUFFS do
		name, rank, icon, count, debuffType, duration, expirationTime, isMine, isStealable = UnitBuff("target", i);
		buttonName = "TargetFrameBuff"..i;
		button = _G[buttonName];
		if ( not button ) then
			if ( not icon ) then
				break;
			else
				button = CreateFrame("Button", buttonName, TargetFrame, "TargetBuffButtonTemplate");
				button.unit = "target";
			end
		end
		if ( icon ) then
			button:SetID(i);

			-- set the icon
			buttonIcon = _G[buttonName.."Icon"];
			buttonIcon:SetTexture(icon);

			-- set the count
			buttonCount = _G[buttonName.."Count"];
			if ( count > 1 ) then
				buttonCount:SetText(count);
				buttonCount:Show();
			else
				buttonCount:Hide();
			end

			-- Handle cooldowns
			buttonCooldown = _G[buttonName.."Cooldown"];
			if ( duration > 0 ) then
				buttonCooldown:Show();
				CooldownFrame_SetTimer(buttonCooldown, expirationTime - duration, duration, 1);
			else
				buttonCooldown:Hide();
			end

			-- Show stealable frame if the target is not a player, the buff is stealable.
			buttonStealable = _G[buttonName.."Stealable"];
			if ( not playerIsTarget and isStealable ) then
				buttonStealable:Show();
			else
				buttonStealable:Hide();
			end

			-- Set the buff to be big if the buff is cast by the player and the target is not the player
			largeBuffList[i] = (isMine and not playerIsTarget and not UnitIsEnemy("target", "player"));

			numBuffs = numBuffs + 1;

			button:ClearAllPoints();
			button:Show();
		else
			button:Hide();
		end
	end

	local color;
	local numDebuffs = 0;
	for i=1, MAX_TARGET_DEBUFFS do
		name, rank, icon, count, debuffType, duration, expirationTime, isMine = UnitDebuff("target", i);
		buttonName = "TargetFrameDebuff"..i;
		button = _G[buttonName];
		if ( not button ) then
			if ( not icon ) then
				break;
			else
				button = CreateFrame("Button", buttonName, TargetFrame, "TargetDebuffButtonTemplate");
				button.unit = "target";
			end
		end
		if ( icon ) then
			button:SetID(i);

			-- set the icon
			buttonIcon = _G[buttonName.."Icon"];
			buttonIcon:SetTexture(icon);

			-- set the count
			buttonCount = _G[buttonName.."Count"];
			if ( count > 1 ) then
				buttonCount:SetText(count);
				buttonCount:Show();
			else
				buttonCount:Hide();
			end

			-- Handle cooldowns
			buttonCooldown = _G[buttonName.."Cooldown"];
			if ( duration > 0 ) then
				buttonCooldown:Show();
				CooldownFrame_SetTimer(buttonCooldown, expirationTime - duration, duration, 1);
			else
				buttonCooldown:Hide();
			end

			-- set debuff type color
			if ( debuffType ) then
				color = DebuffTypeColor[debuffType];
			else
				color = DebuffTypeColor["none"];
			end
			buttonBorder = _G[buttonName.."Border"];
			buttonBorder:SetVertexColor(color.r, color.g, color.b);

			-- Set the buff to be big if the buff is cast by the player
			largeDebuffList[i] = isMine;

			numDebuffs = numDebuffs + 1;

			button:ClearAllPoints();
			button:Show();
		else
			button:Hide();
		end
	end

	TargetFrame.auraRows = 0;
	local haveTargetofTarget = TargetofTargetFrame:IsShown();
	local maxRowWidth;
	-- update buff positions
	maxRowWidth = ( haveTargetofTarget and TOT_AURA_ROW_WIDTH ) or AURA_ROW_WIDTH;
	TargetFrame_UpdateAuraPositions("TargetFrameBuff", numBuffs, numDebuffs, largeBuffList, TargetFrame_UpdateBuffAnchor, maxRowWidth, 3);
	-- update debuff positions
	maxRowWidth = ( haveTargetofTarget and TargetFrame.auraRows < NUM_TOT_AURA_ROWS and TOT_AURA_ROW_WIDTH ) or AURA_ROW_WIDTH;
	TargetFrame_UpdateAuraPositions("TargetFrameDebuff", numDebuffs, numBuffs, largeDebuffList, TargetFrame_UpdateDebuffAnchor, maxRowWidth, 4);
	-- update the spell bar position
	Target_Spellbar_AdjustPosition();
end

function TargetFrame_UpdateAuraPositions(auraName, numAuras, numOppositeAuras, largeAuraList, updateFunc, maxRowWidth, offsetX)
	-- a lot of this complexity is in place to allow the auras to wrap around the target of target frame if it's shown

	-- Position auras
	local size;
	local offsetY = AURA_OFFSET_Y;
	-- current width of a row, increases as auras are added and resets when a new aura's width exceeds the max row width
	local rowWidth = 0;
	local firstBuffOnRow = 1;
	for i=1, numAuras do
		-- update size and offset info based on large aura status
		if ( largeAuraList[i] ) then
			size = LARGE_AURA_SIZE;
			offsetY = AURA_OFFSET_Y + AURA_OFFSET_Y;
		else
			size = SMALL_AURA_SIZE;
		end

		-- anchor the current aura
		if ( i == 1 ) then
			rowWidth = size;
			TargetFrame.auraRows = TargetFrame.auraRows + 1;
		else
			rowWidth = rowWidth + size + offsetX;
		end
		if ( rowWidth > maxRowWidth ) then
			-- this aura would cause the current row to exceed the max row width, so make this aura
			-- the start of a new row instead
			updateFunc(auraName, i, numOppositeAuras, firstBuffOnRow, size, offsetX, offsetY);

			rowWidth = size;
			TargetFrame.auraRows = TargetFrame.auraRows + 1;
			firstBuffOnRow = i;
			offsetY = AURA_OFFSET_Y;

			if ( TargetFrame.auraRows > NUM_TOT_AURA_ROWS ) then
				-- if we exceed the number of tot rows, then reset the max row width
				-- note: don't have to check if we have tot because AURA_ROW_WIDTH is the default anyway
				maxRowWidth = AURA_ROW_WIDTH;
			end
		else
			updateFunc(auraName, i, numOppositeAuras, i - 1, size, offsetX, offsetY);
		end
	end
end

function TargetFrame_UpdateBuffAnchor(buffName, index, numDebuffs, anchorIndex, size, offsetX, offsetY)
	local buff = _G[buffName..index];

	if ( index == 1 ) then
		if ( UnitIsFriend("player", "target") or numDebuffs == 0 ) then
			-- unit is friendly or there are no debuffs...buffs start on top
			buff:SetPoint("TOPLEFT", TargetFrame, "BOTTOMLEFT", AURA_START_X, AURA_START_Y);
		else
			-- unit is not friendly and we have debuffs...buffs start on bottom
			buff:SetPoint("TOPLEFT", TargetFrameDebuffs, "BOTTOMLEFT", 0, -offsetY);
		end
		TargetFrameBuffs:SetPoint("TOPLEFT", buff, "TOPLEFT", 0, 0);
		TargetFrameBuffs:SetPoint("BOTTOMLEFT", buff, "BOTTOMLEFT", 0, -AURA_OFFSET_Y);
	elseif ( anchorIndex ~= (index-1) ) then
		-- anchor index is not the previous index...must be a new row
		buff:SetPoint("TOPLEFT", _G[buffName..anchorIndex], "BOTTOMLEFT", 0, -offsetY);
		TargetFrameBuffs:SetPoint("BOTTOMLEFT", buff, "BOTTOMLEFT", 0, -AURA_OFFSET_Y);
	else
		-- anchor index is the previous index
		buff:SetPoint("TOPLEFT", _G[buffName..anchorIndex], "TOPRIGHT", offsetX, 0);
	end

	-- Resize
	buff:SetWidth(size);
	buff:SetHeight(size);
end

function TargetFrame_UpdateDebuffAnchor(debuffName, index, numBuffs, anchorIndex, size, offsetX, offsetY)
	local buff = _G[debuffName..index];

	if ( index == 1 ) then
		if ( UnitIsFriend("player", "target") and numBuffs > 0 ) then
			-- unit is friendly and there are buffs...debuffs start on bottom
			buff:SetPoint("TOPLEFT", TargetFrameBuffs, "BOTTOMLEFT", 0, -offsetY);
		else
			-- unit is not friendly or there are no buffs...debuffs start on top
			buff:SetPoint("TOPLEFT", TargetFrame, "BOTTOMLEFT", AURA_START_X, AURA_START_Y);
		end
		TargetFrameDebuffs:SetPoint("TOPLEFT", buff, "TOPLEFT", 0, 0);
		TargetFrameDebuffs:SetPoint("BOTTOMLEFT", buff, "BOTTOMLEFT", 0, -AURA_OFFSET_Y);
	elseif ( anchorIndex ~= (index-1) ) then
		-- anchor index is not the previous index...must be a new row
		buff:SetPoint("TOPLEFT", _G[debuffName..anchorIndex], "BOTTOMLEFT", 0, -offsetY);
		TargetFrameDebuffs:SetPoint("BOTTOMLEFT", buff, "BOTTOMLEFT", 0, -AURA_OFFSET_Y);
	else
		-- anchor index is the previous index
		buff:SetPoint("TOPLEFT", _G[debuffName..(index-1)], "TOPRIGHT", offsetX, 0);
	end

	-- Resize
	buff:SetWidth(size);
	buff:SetHeight(size);
	local debuffFrame =_G[debuffName..index.."Border"];
	debuffFrame:SetWidth(size+2);
	debuffFrame:SetHeight(size+2);
end
